#include "XComponentBackend.h"
#include "video/VideoProcessor.h"
#include "audio/AudioProcessor.h"
#include "control/touch_screen/PassThroughController.h"
#include "control/mouse/MouseController.h"
#include "control/keyboard/KeyboardController.h"
#include "core/utils/AVUtils.h"
#include "core/utils/NapiUtils.h"
#include "core/utils/XComponentUtils.h"

#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN  0x0
#define LOG_TAG     "XComponentBackend"
#include <hilog/log.h>


XComponentBackend::XComponentBackend(std::string_view id, OHNativeWindow *nativeWindow) :
        id(id), nativeWindow(nativeWindow),
        videoProcessor(std::make_unique<VideoProcessor>(this, nativeWindow)),
        audioProcessor(std::make_unique<AudioProcessor>(this)),
        touchHandler(std::make_unique<PassThroughController>()),
        mouseController(std::make_unique<MouseController>()),
        keyboardController(std::make_unique<KeyboardController>()) {
}

XComponentBackend::~XComponentBackend() {
    OH_LOG_DEBUG(LOG_APP, "Stopping XComponentBackend %{public}s", id.c_str());
    stopFlag = true;
    keyboardController = nullptr;
    mouseController = nullptr;
    touchHandler = nullptr;
    audioProcessor = nullptr;
    videoProcessor = nullptr;
    OH_LOG_DEBUG(LOG_APP, "Stopped XComponentBackend %{public}s", id.c_str());
}

napi_status XComponentBackend::doInit(napi_env env, napi_value exports, std::string &xcompId) {
    OH_NativeXComponent *comp;
    {
        napi_value exportInstance = nullptr;
        NAPI_RETURN_IF_NOT_OK(napiCallResult, napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance));
        NAPI_RETURN_IF_NOT_OK(napiCallResult, napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&comp)));
    }
    {
        const auto idOpt = XComponentUtils::getId(comp);
        if (!idOpt) {
            OH_LOG_ERROR(LOG_APP, "Failed to extract id from environment XComponent");
            return napi_invalid_arg;
        }
        xcompId = *idOpt;
    }
    OH_LOG_INFO(LOG_APP, "Found XComponent %{public}s", xcompId.c_str());
    /* register callbacks */
    XCOMP_RETURN_IF_NOT_OK(napi_generic_failure, RegisterCallback(comp, const_cast<OH_NativeXComponent_Callback*>(&XComponentCallbacks::callbacks)));
    XCOMP_RETURN_IF_NOT_OK(napi_generic_failure, RegisterMouseEventCallback(comp, const_cast<OH_NativeXComponent_MouseEvent_Callback*>(&XComponentCallbacks::mouseEventCallbacks)));
    XCOMP_RETURN_IF_NOT_OK(napi_generic_failure, RegisterKeyEventCallbackWithResult(comp, XComponentCallbacks::KeyEventCallback));
    XCOMP_RETURN_IF_NOT_OK(napi_generic_failure, RegisterUIInputEventCallback(comp, XComponentCallbacks::UIInputAxisEventCallback, ARKUI_UIINPUTEVENT_TYPE_AXIS));
    /* one-time set-ups */
    {
        OH_NativeXComponent_ExpectedRateRange rateRange = {
            .min = 120,
            .max = 120,
            .expected = 120,
        };
        XCOMP_RETURN_IF_NOT_OK(napi_generic_failure, SetExpectedFrameRateRange(comp, &rateRange));
    }
    return napi_ok;
}

void XComponentBackend::setTouchHandler(std::unique_ptr<AbstractTouchScreenControlHandler> touchHandler) {
    this->touchHandler = std::move(touchHandler);
}
